import AttributeUtil from '@/AttributeUtil'
import DamageUtil from '@/DamageUtil'
import RandomUtil from '@/RandomUtil'
import UnitStateUtil from '@/UnitStateUtil'
import CusEventTypes from './Configs/EventConfig'
import UnitCommon from '../Common/UnitCommon'
import MathCommon from '../Common/MathCommon'
import SelectCommon from '../Common/SelectCommon'
import {
    attackDamageConfig,
    magicDamageConfig,
    physicalDamageConfig,
} from './Configs/DamageConfig'
import DamageTotal from './DamageTotal'
import SelectUtil from '@/SelectUtil'

declare global {
    type magicTypes =
        | 'light'
        | 'dark'
        | 'fire'
        | 'ice'
        | 'poision'
        | 'wind'
        | 'lighting'
        | 'earth'
        | 'arcane'
    interface DamageEventData {
        target: unit
        source: unit
        initDamage: number
        calcDamage: number
        lastDamage: number
        isAttack?: boolean
        isPhysical?: boolean
        isAttach?: boolean
        isRange?: boolean
        isSplit?: boolean
        isAoe?: boolean
        ignoreDogge?: boolean
        isCrited?: boolean
        isReflect?: boolean
        hasDogged?: boolean
        技能ID?: number
        abilityId?: string
        magicType?: magicTypes
        /** 是否为原生的伤害事件 */
        isNative?: boolean
        isDot?: boolean
        isStop?: boolean
        // [key: string]: any;
    }
}

export default class DamageSystem {
    /** 是否启用伤害系统，用于阻断伤害的死循环 */
    enable: boolean = true

    config: {
        /** 伤害在一定范围之间浮动 */
        enableDamageFloat: boolean
        /** 伤害浮动幅度 */
        damageFloatRange: number
    } = {
        enableDamageFloat: true,
        damageFloatRange: 0.1,
    }
    private static prevProcess: ((data: DamageEventData) => void)[] = []
    private static lastProcess: ((data: DamageEventData) => void)[] = []
    addPrevProceess(cb: (data: DamageEventData) => void) {
        DamageSystem.prevProcess.push(cb)
    }
    addLastProcess(cb: (data: DamageEventData) => void) {
        DamageSystem.lastProcess.push(cb)
    }
    constructor() {
        se.onUnitDamaged((e) => {
            if (e.damage < 1) return
            if (!this.enable) return

            let data: DamageEventData = {
                target: e.trigUnit,
                source: e.damageSource,
                initDamage: e.damage,
                calcDamage: e.damage,
                lastDamage: e.damage,
                isAttack:
                    0 != EXGetEventDamageData(EVENT_DAMAGE_DATA_IS_ATTACK),
                isRange: 0 != EXGetEventDamageData(EVENT_DAMAGE_DATA_IS_RANGED),
                isPhysical: DamageUtil.isEventPhysicalDamageType(),
                isCrited: false,
                isAttach: false,
                isNative: true,
            }
            this.calcDamage(data)
        })
    }
    calcDamageInrease(data: DamageEventData, sourceAttr: AppAttribute) {
        let lastDamage = (data.lastDamage = data.initDamage)

        // 计算技能强化系统的增伤
        if (data.abilityId) {
            lastDamage *=
                1 +
                ((sourceAttr[data.abilityId + '_inc_damage'] as number) ?? 0)
        }

        if (data.isDot) {
            lastDamage *= 1 + (sourceAttr?.持续伤害增强 ?? 0)
        }

        // 精英伤害增强
        if (UnitCommon.是否精英单位(data.target)) {
            lastDamage *= 1 + (sourceAttr?.精英伤害增强 ?? 0)
        }

        // 伤害流程
        // 攻击增伤
        if (data.isAttack || data.isAttach) {
            lastDamage *= UnitCommon.取物理伤害增强(data.source)
            if (
                RandomUtil.randomPercent() <=
                UnitCommon.取物理暴击率(data.source)
            ) {
                data.isCrited = true
                SetUnitAnimation(data.source, 'Slam')
            }
            if (!data.isSplit) {
                this.applySplitDamage(data)
            }
        }

        if (data.isReflect) {
            lastDamage *= UnitCommon.取反伤倍率(data.source)
        } else if (data.isSplit) {
        }
        // 技能增伤
        else if (data.isPhysical && !data.isAttack && !data.isAttach) {
            lastDamage *= UnitCommon.取物理伤害增强(data.source)
            // 计算暴击
            if (
                RandomUtil.randomPercent() <=
                UnitCommon.取物理暴击率(data.source)
            ) {
                data.isCrited = true
            }
        } else {
            lastDamage *= UnitCommon.取法术伤害增强(data.source)

            // 特定法术增强
            if (data.magicType == 'light') {
                lastDamage += sourceAttr?.光系伤害增强 ?? 0
                lastDamage *= 1 + (sourceAttr?.光系伤害增强 ?? 0)
            }
            if (data.magicType == 'ice') {
                lastDamage += sourceAttr?.冰系伤害增强 ?? 0
                lastDamage *= 1 + (sourceAttr?.冰系伤害增强百分比 ?? 0)
            }
            if (data.magicType == 'fire') {
                lastDamage += sourceAttr?.火系伤害增强 ?? 0
                lastDamage *= 1 + (sourceAttr?.火系伤害增强百分比 ?? 0)
            }
            if (data.magicType == 'poision') {
                lastDamage += sourceAttr?.毒系伤害增强 ?? 0
                lastDamage *= 1 + (sourceAttr?.毒系伤害增强百分比 ?? 0)
            }
            if (data.magicType == 'earth') {
                lastDamage += sourceAttr?.土系伤害增强 ?? 0
                lastDamage *= 1 + (sourceAttr?.土系伤害增强百分比 ?? 0)
            }
            if (data.magicType == 'arcane') {
                lastDamage += sourceAttr?.奥术伤害增强 ?? 0
                lastDamage *= 1 + (sourceAttr?.奥术伤害增强百分比 ?? 0)
            }
            if (data.magicType == 'lighting') {
                lastDamage += sourceAttr?.闪电伤害增强 ?? 0
                lastDamage *= 1 + (sourceAttr?.闪电伤害增强百分比 ?? 0)
            }
            if (data.magicType == 'wind') {
                lastDamage += sourceAttr?.风系伤害增强 ?? 0
                lastDamage *= 1 + (sourceAttr?.风系伤害增强百分比 ?? 0)
            }
            if (data.magicType == 'dark') {
                lastDamage += sourceAttr?.暗系伤害增强 ?? 0
                lastDamage *= 1 + (sourceAttr?.暗系伤害增强百分比 ?? 0)
            }

            // 计算暴击
            if (
                RandomUtil.randomPercent() <=
                UnitCommon.取法术暴击率(data.source)
            ) {
                data.isCrited = true
            }
        }

        // 暴击增伤
        if (data.isCrited) {
            se.emit(CusEventTypes.单位受到暴击, data)
            if (data.isPhysical) {
                lastDamage *= UnitCommon.取物理暴击伤害(data.source)
            } else {
                lastDamage *= UnitCommon.取法术暴击伤害(data.source)
            }
        }

        lastDamage *= sourceAttr?.全伤害倍率 ?? 1

        data.lastDamage = lastDamage
        data.calcDamage = lastDamage
        // 派发受伤之前的事件
        se.emit(CusEventTypes.onBeforTakeDamage, data)
    }

    calcDamageReduce(
        data: DamageEventData,
        sourceAttr: AppAttribute,
        target属性: AppAttribute
    ) {
        let lastDamage = data.lastDamage
        // 计算精英伤害减免
        if (UnitCommon.是否精英单位(data.source)) {
            if (target属性?.精英伤害减免) {
                lastDamage *= 1 - (target属性?.精英伤害减免 ?? 0)
            }
        }

        if (data.isReflect) {
        }
        // 计算伤害减免
        else if (data.isPhysical) {
            let 护甲 = UnitCommon.取总护甲(data.target)
            // 护甲穿透
            护甲 -= sourceAttr?.护甲穿透 ?? 0
            护甲 *= 1 - (sourceAttr?.护甲穿透百分比 ?? 0)
            let 护甲减免 = MathCommon.calulateReduce(
                护甲,
                ARMOR_DAMAGE_REDUCE,
                1
            )
            lastDamage *= 1 - 护甲减免
            lastDamage *= 1 - (target属性?.物理伤害减免 ?? 0)
        } else {
            let 魔抗 = UnitCommon.取总魔抗(data.target)
            // 法术穿透
            魔抗 -= sourceAttr?.法术穿透 ?? 0
            魔抗 *= 1 - (sourceAttr?.法术穿透百分比 ?? 0)
            let 魔抗减免 = MathCommon.calulateReduce(
                魔抗,
                RESISTANCE_DAMAGE_REDUCE,
                1
            )
            lastDamage *= 1 - 魔抗减免
            lastDamage *= 1 - (target属性?.法术伤害减免 ?? 0)
            // 多种特定法术伤害减免
            if (data.magicType == 'light') {
                lastDamage -= target属性?.光系抗性 ?? 0
                lastDamage *= 1 - (target属性?.光系伤害减免百分比 ?? 0)
            }
            if (data.magicType == 'ice') {
                lastDamage -= target属性?.冰系抗性 ?? 0
                lastDamage *= 1 - (target属性?.冰系伤害减免百分比 ?? 0)
            }
            if (data.magicType == 'poision') {
                lastDamage -= target属性?.毒系抗性 ?? 0
                lastDamage *= 1 - (target属性?.毒系伤害减免百分比 ?? 0)
            }
            if (data.magicType == 'earth') {
                lastDamage -= target属性?.土系抗性 ?? 0
                lastDamage *= 1 - (target属性?.土系伤害减免百分比 ?? 0)
            }
            if (data.magicType == 'arcane') {
                lastDamage -= target属性?.奥术抗性 ?? 0
                lastDamage *= 1 - (target属性?.奥术伤害减免百分比 ?? 0)
            }
            if (data.magicType == 'fire') {
                lastDamage -= target属性?.火系抗性 ?? 0
                lastDamage *= 1 - (target属性?.火系伤害减免百分比 ?? 0)
            }
            if (data.magicType == 'lighting') {
                lastDamage -= target属性?.闪电抗性 ?? 0
                lastDamage *= 1 - (target属性?.闪电伤害减免百分比 ?? 0)
            }
            if (data.magicType == 'wind') {
                lastDamage -= target属性?.风系抗性 ?? 0
                lastDamage *= 1 - (target属性?.风系伤害减免百分比 ?? 0)
            }
            if (data.magicType == 'dark') {
                lastDamage -= target属性?.暗系抗性 ?? 0
                lastDamage *= 1 - (target属性?.暗系伤害减免百分比 ?? 0)
            }
        }

        lastDamage *= 1 - (target属性?.全伤害减免 ?? 0)

        // AOE
        if (data.isAoe) {
            lastDamage *= 1 - (target属性?.AOE伤害减免 ?? 0)
        }
        data.lastDamage = Math.max(0, lastDamage)
    }
    calcDamage(data: DamageEventData) {
        // 目标死亡则不再计算
        if (
            GetUnitState(data.target, UNIT_STATE_LIFE) <= 0 ||
            !IsUnitAliveBJ(data.target)
        )
            return
        // 单位被攻击前
        if (data.isAttack || data.isAttach) {
            se.emit(CusEventTypes.onBeforApplyAttack, data)
        }

        let target属性 = AttributeUtil.getUnitAttribute(data.target)
        let sourceAttr = AttributeUtil.getUnitAttribute(data.source)
        this.calcDamageInrease(data, sourceAttr)
        this.calcDamageReduce(data, sourceAttr, target属性)
        let lastDamage = data.lastDamage
        // 攻击伤害计算闪避
        if (data.isAttack || data.isAttach) {
            if (!data.ignoreDogge) {
                let 闪避 = UnitCommon.取总闪避(data.target)
                if (RandomUtil.randomPercent() <= 闪避) {
                    data.hasDogged = true
                }
            }
        }
        if (this.config.enableDamageFloat) {
            lastDamage *= RandomUtil.nextReal(
                1 - this.config.damageFloatRange,
                this.config.damageFloatRange + 1
            )
        }

        data.lastDamage = Math.max(0, lastDamage)

        let hitRate = UnitCommon.取命中率(data.source)
        let r = RandomUtil.randomPercent()
        if (r > hitRate) {
            data.hasDogged = true
        }

        for (let fn of DamageSystem.prevProcess) {
            fn(data)
        }

        // 派发被攻击事件
        if (data.isAttack || data.isAttach) {
            se.emit(CusEventTypes.单位受到攻击伤害, data)
        }
        // 发布自己的事件
        se.emit(CusEventTypes.onTakeDamage, data)

        for (let fn of DamageSystem.lastProcess) {
            fn(data)
        }

        // 扣血流程
        if (!data.hasDogged) {
            // 计算吸血
            // 反伤分裂不吸血
            if (!data.isReflect && !data.isSplit) {
                let 吸血 = sourceAttr?.法术吸血 ?? 0
                if (data.isAttack || data.isAttach) {
                    吸血 = sourceAttr?.物理吸血 ?? 0
                }
                if (吸血 > 0) {
                    this.applyHeal(
                        data.source,
                        data.source,
                        lastDamage * 吸血,
                        ''
                    )
                }
            }
            let life = UnitStateUtil.getLife(data.target)
            if (data.lastDamage >= life) {
                se.emit(CusEventTypes.受到致命伤害, data)
            }
            if (data.isNative) {
                EXSetEventDamage(data.lastDamage)
            } else {
                this.enable = false
                // UnitStateUtil.setLife(data.target, life - data.lastDamage);
                UnitDamageTarget(
                    data.source,
                    data.target,
                    data.lastDamage,
                    false,
                    data.isRange,
                    ATTACK_TYPE_CHAOS,
                    DAMAGE_TYPE_UNIVERSAL,
                    WEAPON_TYPE_WHOKNOWS
                )
                this.enable = true
            }
            if (data.lastDamage >= life) {
                se.emit(CusEventTypes.单位死亡, data)
            }
            if (IsUnitEnemy(data.source, Player(0))) {
                // source是玩家的敌人，即玩家单位承受了伤害
                DamageTotal.addTakeDamageRecord(
                    game_wave,
                    GetPlayerId(GetOwningPlayer(data.target)),
                    data.lastDamage
                )
            }
            if (IsUnitEnemy(data.target, Player(0))) {
                // target是玩家的敌人，即玩家单位造成了伤害
                DamageTotal.addMakeDamageRecord(
                    game_wave,
                    GetPlayerId(GetOwningPlayer(data.source)),
                    data.lastDamage
                )
            }
        } else {
            // 闪避
            se.emit(CusEventTypes.单位闪避攻击, data)
            if (data.isNative) {
                EXSetEventDamage(0.01)
            } else {
                this.enable = false
                UnitDamageTarget(
                    data.source,
                    data.target,
                    0.01,
                    false,
                    data.isRange,
                    ATTACK_TYPE_CHAOS,
                    DAMAGE_TYPE_UNIVERSAL,
                    WEAPON_TYPE_WHOKNOWS
                )
                this.enable = true
            }
        }
    }

    applySplitDamage(数据: DamageEventData, 范围 = 150) {
        if (数据.isSplit) return
        if (数据.isAoe) return
        if (数据.isReflect) return
        let 属性 = AttributeUtil.getUnitAttribute(数据.source)
        if (属性 == null) return
        if (属性.分裂伤害系数 == null) return
        范围 += 属性.分裂范围系数 ?? 0
        let 分裂目标 = SelectCommon.getAllyUnitInRangeExcludeCenterUnit(
            数据.target,
            范围
        )
        if (分裂目标.length == 0) return
        for (let item of 分裂目标) {
            let 分裂数据 = Object.assign({}, 数据)
            分裂数据.target = item
            分裂数据.isAoe = true
            分裂数据.isSplit = true
            分裂数据.isNative = false
            this.applyDamage(
                item,
                数据.source,
                数据.initDamage * 属性.分裂伤害系数,
                分裂数据
            )
        }
    }

    applyDamage<T extends Partial<DamageEventData> = Partial<DamageEventData>>(
        目标: unit,
        来源: unit,
        伤害: number,
        选项: T
    ) {
        let 默认数据: Partial<DamageEventData> = {
            isAttack: false,
            isRange: false,
            isPhysical: DamageUtil.isEventPhysicalDamageType(),
            isCrited: false,
            isAttach: false,
        }

        选项 = Object.assign(默认数据, 选项)

        let 伤害数据: DamageEventData = {
            ...选项,
            target: 目标,
            source: 来源,
            calcDamage: 伤害,
            initDamage: 伤害,
            lastDamage: 伤害,
        }
        this.calcDamage(伤害数据)
    }
    applyDotHeal(tar: unit, source: unit, value: number, abilityId?: string) {
        this.applyHeal(tar, source, value, abilityId, {
            isDot: true,
        })
    }
    applyHeal<T extends Partial<DamageEventData> = Partial<DamageEventData>>(
        目标: unit,
        来源: unit,
        数值: number,
        abilityId?: string,
        选项?: T
    ) {
        let 默认数据: Partial<DamageEventData> = {
            isCrited: false,
        }
        选项 = Object.assign(默认数据, 选项)
        let 数据: DamageEventData = {
            target: 目标,
            source: 来源,
            initDamage: 数值,
            calcDamage: 数值,
            lastDamage: 数值,
            isDot: false,
            abilityId,
            ...选项,
        }
        if (选项.isDot) {
            数值 *= 1 + UnitCommon.取持续治疗增强(来源)
        }

        let attr = AttributeUtil.getUnitAttribute(来源)
        if (attr != null) {
            数值 *=
                1 +
                ((attr?.[(数据.abilityId ?? '') + '_inc_damage'] as number) ??
                    0)
        }

        if (RandomUtil.randomPercent() <= UnitCommon.取法术暴击率(来源)) {
            数据.isCrited = true
        }
        if (数据.isCrited) {
            数值 *= UnitCommon.取法术暴击伤害(来源)
        }

        数值 *= 1 + (AttributeUtil.getUnitAttribute(来源)?.治疗增强 ?? 0)
        数值 *= 1 + (AttributeUtil.getUnitAttribute(目标)?.受到治疗增强 ?? 0)

        // 加血
        let 生命 = GetUnitState(目标, UNIT_STATE_LIFE)
        生命 += 数值
        let 最大生命 = GetUnitState(目标, UNIT_STATE_MAX_LIFE)
        if (生命 > 最大生命) {
            生命 = 最大生命
        }
        SetUnitState(目标, UNIT_STATE_LIFE, 生命)
        数据.calcDamage = 数值
        数据.lastDamage = 数值

        // 产生治疗仇恨
        HatredySystemInstance.HealActionIncreaseHatred(来源, 数值)

        se.emit(CusEventTypes.单位受到治疗, 数据)
        if (IsUnitAlly(数据.source, Player(0))) {
            DamageTotal.addHealRecord(
                game_wave,
                GetPlayerId(GetOwningPlayer(数据.source)),
                数据.lastDamage
            )
        }
    }
    applyAoeDamageByUnit<
        T extends Partial<DamageEventData> = Partial<DamageEventData>
    >(
        source: unit,
        range: number,
        value: number,
        data: T,
        centerX: number,
        centerY: number,
        abilityId?: string
    ) {
        data.isAoe = true
        this.applyAoeDamage(
            SelectUtil.getEnemyUnitsInRange(source, range, centerX, centerY),
            source,
            value,
            data
        )
    }
    applyAoeDamage<
        T extends Partial<DamageEventData> = Partial<DamageEventData>
    >(数组: unit[], 来源: unit, 伤害: number, 选项: T) {
        选项.isAoe = true
        for (let e of 数组) {
            this.applyDamage(e, 来源, 伤害, 选项)
        }
    }
    applyFireDamage(
        目标: unit,
        来源: unit,
        伤害: number,
        abilityId?: string,
        isDot: boolean = false
    ) {
        this.applyDamage(目标, 来源, 伤害, {
            ...magicDamageConfig,
            magicType: 'fire',
            isDot: isDot,
            abilityId,
        })
    }
    applyLightDamage(
        目标: unit,
        来源: unit,
        伤害: number,
        abilityId?: string,
        isDot: boolean = false
    ) {
        this.applyDamage(目标, 来源, 伤害, {
            ...magicDamageConfig,
            magicType: 'light',
            isDot: isDot,
            abilityId,
        })
    }
    applyIceDamage(
        目标: unit,
        来源: unit,
        伤害: number,
        abilityId?: string,
        isDot: boolean = false
    ) {
        this.applyDamage(目标, 来源, 伤害, {
            ...magicDamageConfig,
            magicType: 'ice',
            isDot: isDot,
            abilityId,
        })
    }
    applyPoisionDamage(
        目标: unit,
        来源: unit,
        伤害: number,
        abilityId?: string,
        isDot: boolean = false
    ) {
        this.applyDamage(目标, 来源, 伤害, {
            ...magicDamageConfig,
            magicType: 'poision',
            isDot: isDot,
            abilityId,
        })
    }
    applyEarthDamage(
        目标: unit,
        来源: unit,
        伤害: number,
        abilityId?: string,
        isDot: boolean = false
    ) {
        this.applyDamage(目标, 来源, 伤害, {
            ...magicDamageConfig,
            magicType: 'earth',
            isDot: isDot,
            abilityId,
        })
    }
    applyArcaneDamage(
        目标: unit,
        来源: unit,
        伤害: number,
        abilityId?: string,
        isDot: boolean = false
    ) {
        this.applyDamage(目标, 来源, 伤害, {
            ...magicDamageConfig,
            magicType: 'arcane',
            isDot: isDot,
            abilityId,
        })
    }
    applyLightingDamage(
        目标: unit,
        来源: unit,
        伤害: number,
        abilityId?: string,
        isDot: boolean = false
    ) {
        this.applyDamage(目标, 来源, 伤害, {
            ...magicDamageConfig,
            magicType: 'lighting',
            isDot: isDot,
            abilityId,
        })
    }
    applyWindDamage(
        目标: unit,
        来源: unit,
        伤害: number,
        abilityId?: string,
        isDot: boolean = false
    ) {
        this.applyDamage(目标, 来源, 伤害, {
            ...magicDamageConfig,
            magicType: 'wind',
            isDot: isDot,
            abilityId,
        })
    }
    applyDarkDamage(
        目标: unit,
        来源: unit,
        伤害: number,
        abilityId?: string,
        isDot: boolean = false
    ) {
        this.applyDamage(目标, 来源, 伤害, {
            ...magicDamageConfig,
            magicType: 'dark',
            isDot: isDot,
            abilityId,
        })
    }
    applyPhysicalDamage(
        目标: unit,
        来源: unit,
        伤害: number,
        abilityId?: string,
        isDot: boolean = false
    ) {
        this.applyDamage(目标, 来源, 伤害, {
            ...physicalDamageConfig,
            isDot: isDot,
            abilityId,
        })
    }
    applyAttackDamage(
        目标: unit,
        来源: unit,
        伤害: number,
        abilityId?: string
    ) {
        this.applyDamage(目标, 来源, 伤害, { ...attackDamageConfig, abilityId })
    }
    applyAttachAttackDamage(
        目标: unit,
        来源: unit,
        伤害: number,
        abilityId?: string
    ) {
        this.applyDamage(目标, 来源, 伤害, {
            isAttach: true,
            isPhysical: true,
            isNative: false,
            abilityId,
        })
    }
    applyAoeMagicDamage(
        目标: unit[],
        来源: unit,
        伤害: number,
        类型: magicTypes = 'dark',
        abilityId?: string,
        isDot: boolean = false
    ) {
        this.applyAoeDamage(目标, 来源, 伤害, {
            magicDamageConfig,
            magicType: 类型,
            isDot: isDot,
            abilityId,
        })
    }
    applyReflectDamage(
        目标: unit,
        来源: unit,
        伤害: number,
        abilityId?: string
    ) {
        this.applyDamage(目标, 来源, 伤害, {
            isAttack: false,
            isReflect: true,
            abilityId,
        })
    }
}
